{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "ba8f01a1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集样本数: 142, 特征数: 13\n",
      "测试集样本数: 36\n"
     ]
    }
   ],
   "source": [
    "from sklearn.datasets import load_wine\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "import pandas as pd\n",
    "\n",
    "# 加载数据集\n",
    "wine = load_wine()\n",
    "X = wine.data\n",
    "y = wine.target\n",
    "feature_names = wine.feature_names\n",
    "target_names = wine.target_names\n",
    "\n",
    "# 划分训练集和测试集 (8:2)\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n",
    "\n",
    "# 标准化数据（KNN对量纲敏感，朴素贝叶斯的高斯分布假设也需要标准化）\n",
    "scaler = StandardScaler()\n",
    "X_train_scaled = scaler.fit_transform(X_train)\n",
    "X_test_scaled = scaler.transform(X_test)\n",
    "\n",
    "# 查看预处理后的数据形状\n",
    "print(f\"训练集样本数: {X_train_scaled.shape[0]}, 特征数: {X_train_scaled.shape[1]}\")\n",
    "print(f\"测试集样本数: {X_test_scaled.shape[0]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "46e78895",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== 测试集评估结果(KNN) ===\n",
      "准确率: 1.0000\n",
      "\n",
      "混淆矩阵:\n",
      "[[12  0  0]\n",
      " [ 0 14  0]\n",
      " [ 0  0 10]]\n",
      "\n",
      "分类报告:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "     class_0       1.00      1.00      1.00        12\n",
      "     class_1       1.00      1.00      1.00        14\n",
      "     class_2       1.00      1.00      1.00        10\n",
      "\n",
      "    accuracy                           1.00        36\n",
      "   macro avg       1.00      1.00      1.00        36\n",
      "weighted avg       1.00      1.00      1.00        36\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 自定义KNN\n",
    "\n",
    "import numpy as np\n",
    "from collections import Counter\n",
    "from sklearn.metrics import accuracy_score, confusion_matrix, classification_report\n",
    "\n",
    "class MyKNN:\n",
    "    \"\"\"\n",
    "    自定义K近邻分类器实现\n",
    "    \"\"\"\n",
    "    def __init__(self, n_neighbors=5):\n",
    "        self.n_neighbors = n_neighbors  # 近邻数K\n",
    "        self.X_train = None\n",
    "        self.y_train = None\n",
    "\n",
    "    def fit(self, X, y):\n",
    "        # 存储训练数据\n",
    "        self.X_train = X\n",
    "        self.y_train = y\n",
    "        return self\n",
    "\n",
    "    def predict(self, X):\n",
    "        # 预测新样本类别\n",
    "        predictions = []\n",
    "        for x in X:\n",
    "            # 计算当前样本与所有训练样本的欧氏距离\n",
    "            distances = [self._euclidean_distance(x, x_train) for x_train in self.X_train]\n",
    "            # 获取距离最近的K个样本的索引\n",
    "            k_indices = np.argsort(distances)[:self.n_neighbors]\n",
    "            # 提取对应的类别标签并统计多数票\n",
    "            k_nearest_labels = self.y_train[k_indices]\n",
    "            most_common_label = Counter(k_nearest_labels).most_common(1)[0][0]\n",
    "            predictions.append(most_common_label)\n",
    "        return np.array(predictions)\n",
    "\n",
    "    @staticmethod\n",
    "    def _euclidean_distance(a, b):\n",
    "        \"\"\"欧氏距离计算\"\"\"\n",
    "        return np.sqrt(np.sum((a - b)**2))\n",
    "\n",
    "\n",
    "\n",
    "custom_knn = MyKNN(n_neighbors=6)\n",
    "custom_knn.fit(X_train_scaled, y_train)\n",
    "y_pred_custom = custom_knn.predict(X_test_scaled)\n",
    "\n",
    "print(\"\\n=== 测试集评估结果(KNN) ===\")\n",
    "print(f\"准确率: {accuracy_score(y_test, y_pred_custom):.4f}\")\n",
    "print(\"\\n混淆矩阵:\")\n",
    "print(confusion_matrix(y_test, y_pred_custom))\n",
    "print(\"\\n分类报告:\")\n",
    "print(classification_report(y_test, y_pred_custom, target_names=target_names))\n",
    "\n",
    "# accuracies = []\n",
    "\n",
    "# for k in range(1, 14):  \n",
    "#     custom_knn = MyKNN(n_neighbors=k)\n",
    "#     custom_knn.fit(X_train_scaled, y_train)\n",
    "#     y_pred = custom_knn.predict(X_test_scaled)\n",
    "#     acc = accuracy_score(y_test, y_pred)\n",
    "#     accuracies.append(acc)\n",
    "\n",
    "# print(accuracies)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eefdc587",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9722222222222222\n",
      "\n",
      "=== 测试集评估结果 ===\n",
      "准确率: 0.9722\n",
      "\n",
      "混淆矩阵:\n",
      "[[12  0  0]\n",
      " [ 1 13  0]\n",
      " [ 0  0 10]]\n",
      "\n",
      "分类报告:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "     class_0       0.92      1.00      0.96        12\n",
      "     class_1       1.00      0.93      0.96        14\n",
      "     class_2       1.00      1.00      1.00        10\n",
      "\n",
      "    accuracy                           0.97        36\n",
      "   macro avg       0.97      0.98      0.97        36\n",
      "weighted avg       0.97      0.97      0.97        36\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from sklearn.metrics import accuracy_score, confusion_matrix, classification_report\n",
    "\n",
    "# 自定义朴素贝叶斯类\n",
    "class GaussianNB:\n",
    "    def __init__(self):\n",
    "        self.classes = None          # 存储类别标签\n",
    "        self.priors = {}             # 先验概率 P(y)\n",
    "        self.means = {}              # 每个类别的特征均值\n",
    "        self.variances = {}          # 每个类别的特征方差\n",
    "\n",
    "    def fit(self, X, y):\n",
    "        self.classes = np.unique(y)\n",
    "        n_samples, n_features = X.shape\n",
    "        \n",
    "        # 计算每个类别的先验概率\n",
    "        for c in self.classes:\n",
    "            X_c = X[y == c]\n",
    "            self.priors[c] = X_c.shape[0] / n_samples\n",
    "            self.means[c] = X_c.mean(axis=0)     # 计算均值\n",
    "            self.variances[c] = X_c.var(axis=0)  # 计算方差\n",
    "        return self\n",
    "\n",
    "    def _gaussian_pdf(self, x, mean, var):\n",
    "        # 计算高斯概率密度函数（对数形式避免数值下溢）\n",
    "        exponent = -0.5 * ((x - mean)**2) / (var + 1e-9)\n",
    "        log_prob = -0.5 * np.log(2 * np.pi * (var + 1e-9)) + exponent\n",
    "        return np.exp(log_prob)  # 返回实际概率\n",
    "\n",
    "    def predict(self, X):\n",
    "        posteriors = []\n",
    "        for c in self.classes:\n",
    "            prior = np.log(self.priors[c])  # 先验概率的对数\n",
    "            # 计算每个特征的条件概率对数并求和\n",
    "            likelihood = np.sum(np.log(self._gaussian_pdf(X, self.means[c], self.variances[c])), axis=1)\n",
    "            posterior = prior + likelihood\n",
    "            posteriors.append(posterior)\n",
    "        # 选择后验概率最大的类别\n",
    "        return self.classes[np.argmax(posteriors, axis=0)]\n",
    "\n",
    "# 实例化模型并训练\n",
    "gnb = GaussianNB()\n",
    "gnb.fit(X_train_scaled, y_train)\n",
    "\n",
    "# 预测测试集\n",
    "y_pred = gnb.predict(X_test_scaled)\n",
    "\n",
    "print(accuracy_score(y_test, y_pred))\n",
    "# 评估模型\n",
    "print(\"\\n=== 测试集评估结果 ===\")\n",
    "print(f\"准确率: {accuracy_score(y_test, y_pred):.4f}\")\n",
    "print(\"\\n混淆矩阵:\")\n",
    "print(confusion_matrix(y_test, y_pred))\n",
    "print(\"\\n分类报告:\")\n",
    "print(classification_report(y_test, y_pred, target_names=target_names))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23921846",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 20934 (\\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 30830 (\\N{CJK UNIFIED IDEOGRAPH-786E}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 29575 (\\N{CJK UNIFIED IDEOGRAPH-7387}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 27169 (\\N{CJK UNIFIED IDEOGRAPH-6A21}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 22411 (\\N{CJK UNIFIED IDEOGRAPH-578B}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 23545 (\\N{CJK UNIFIED IDEOGRAPH-5BF9}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 27604 (\\N{CJK UNIFIED IDEOGRAPH-6BD4}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 31867 (\\N{CJK UNIFIED IDEOGRAPH-7C7B}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 21035 (\\N{CJK UNIFIED IDEOGRAPH-522B}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 24471 (\\N{CJK UNIFIED IDEOGRAPH-5F97}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 20998 (\\N{CJK UNIFIED IDEOGRAPH-5206}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 21508 (\\N{CJK UNIFIED IDEOGRAPH-5404}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 25351 (\\N{CJK UNIFIED IDEOGRAPH-6307}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_719322/1584388312.py:55: UserWarning: Glyph 26631 (\\N{CJK UNIFIED IDEOGRAPH-6807}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20934 (\\N{CJK UNIFIED IDEOGRAPH-51C6}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 30830 (\\N{CJK UNIFIED IDEOGRAPH-786E}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 29575 (\\N{CJK UNIFIED IDEOGRAPH-7387}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 31867 (\\N{CJK UNIFIED IDEOGRAPH-7C7B}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21035 (\\N{CJK UNIFIED IDEOGRAPH-522B}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24471 (\\N{CJK UNIFIED IDEOGRAPH-5F97}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20998 (\\N{CJK UNIFIED IDEOGRAPH-5206}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 27169 (\\N{CJK UNIFIED IDEOGRAPH-6A21}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 22411 (\\N{CJK UNIFIED IDEOGRAPH-578B}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 23545 (\\N{CJK UNIFIED IDEOGRAPH-5BF9}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 27604 (\\N{CJK UNIFIED IDEOGRAPH-6BD4}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21508 (\\N{CJK UNIFIED IDEOGRAPH-5404}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 25351 (\\N{CJK UNIFIED IDEOGRAPH-6307}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/jzhong/.local/lib/python3.10/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 26631 (\\N{CJK UNIFIED IDEOGRAPH-6807}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n"
     ]
    },
    {
     "data": {
      "image/png": "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tHpsXV1dvva1r6W2tjbnnHNOunbtmltvvTXHH398brjhhuy5555NfR977LGccMIJ6du3b6688sq8++67GTNmTL72ta/lrrvuavYf2QsuuCC/+tWvMmzYsOy44475y1/+ku9///t5//33c/LJJzf1mzZtWo455piss846ueiii7LGGmvk6quvzsCBA3PnnXe22OYHAABYuXx0pd66667b1F5bW5uk5cq+JVFVVZWtttpq2QqkmUKhkIrKdpl5/8/TMP2tUpezUqjsvH7W2f/obL311ivFiiiaM2dbWpnmbF1dXf79739n+pz3U5g3t6S1lNI7s99LfcO8vDFrRjq0q8o67dfMvNp3k3nzkiTFefVpmPluivPqM2/amws8x/w+86attSJLXzHatUu7mq5p3779Iudsu3btstlmm2WNNdZocWzSpEmtv9xSFQkAJbDxxhvnr3/9awqFQqZNm7ZEAd8dd9yRl156KePHj8+uu+6aJOnZs2cOOeSQ/PCHP2x2rosvvjhbbLFFxo4dm3btPvxP5SabbJKjjz46d955Z4455pgkycsvv5w777wzp59+eo4//vimc86YMSM//elP079//3Tq1ClJcu2112b69OkZP358Nt544yTJ7rvvnv333z9jx47Nj370o2V9ewAAgDY0/0t5r7zySrp3797UPnny5BQKhWX60l6hUEjHjh2XuUZaapj+Vua983qpy1ipdOjQodQlsAjmbEsrw5ydv9psXmND0rD6BuTzGhvSmKS+YV6qKir/r3FeivP+b4VlsZhiw7wP/z1vIasu/6/PQo+XsflrGRe1OrGysjIVFRXp0KFD2rdv3/IcS7AjwKq5BhKAVVKhUFjqbW/uv//+dOvWrSncSz78tsyXvvSlPPXUU3nzzQ+/VfTmm2/m6aefziGHHNIU7iXJbrvtli222CL3339/s3MWi8Ucdthhza512GGHZe7cuXnwwQeb9e3Zs2dTuJcka621Vj7/+c/nT3/6U+b93zedAACAldOmm26aLbfcMr/97W+btd9zzz3Zaaed0qVLlxJVBgCsjqzgA2C18PLLL2f33Xdv0b7NNts0Hd9ggw3y0ksvNWv/eN8nn3yy2Tm7dOmS9dZbb6HnTJK5c+dm6tSp2X///Rd4zrlz5+a1115Lt27dlvLVAQAAS2LOnDmZOHFikuT111/PrFmzcu+99yZJ9txzz3Tp0iUjRozIhAkT8txzzzWNO+2003L66adns802y1577ZU//OEPeeihh3LttdeW5HUAAKsvAR8Aq4UZM2Y0PTPjo+a3zZgxo9m/F9S3U6dOTccXdc6OHTumqqqqqe/MmTNTLBabtutc1PUBAIC29+6772bYsGHN2ub/fNNNN6Vnz55pbGxMQ0NDsz5f+MIXMnfu3Fx11VW57rrrsvnmm+eyyy7LPvvss8JqBwBIBHwArEYWtb3nx48trG9r+y2p5XUeAABg8TbZZJO8+OKLi+wzevTojB49ukX7oYcemkMPPbStSgOAlVZFRUVeefnlHPLZA1LI/93PKhaTfPhcwoaGxjQ0NubFSZPT+wtfWuA5GhoaU+E+2HIh4ANgtfDx1XfzzZw5M8l/VtLNX2W3oL4fX7HXqVOnPP/88y36zZ49O/X19U3nWmeddVIoFFp1fQAAAABYGW3ebYv85s/3J0k6Vq2Rrh3Xzrxpb6Y4r75Zv8f++55SlLfaqSh1AQCwInzyk59ser7eR81v23rrrZv6JVngt3lfeumlpuPz+06bNi1vv/32Is/Zvn37bL755gu8/osvvpj27dtn0003XZqXBQAAAACshgR8AKwW9t9//7zyyiv5xz/+0dQ2b968/PrXv87OO++cDTbYIEmywQYbZKeddspvfvObZs/b+Pvf/54pU6bkc5/7XFPbfvvtl0KhkLvvvrvZtX75y1+mffv2+fSnP93s+o888kj+/e9/N7XNmjUr9913Xz772c+mXTuL6gEAAACA1nE3EYCyMnHixMyZMyfvv/9+kmTSpEm59957kyR9+/ZNhw4dMmLEiEyYMCH33XdfNt544yTJEUcckdtuuy3Dhg3LGWecka5du+a2227LlClTcsMNNzS7xvDhw/ONb3wjw4YNyzHHHJN33303l1xyST75yU/m8MMPb+q39dZb54gjjsgVV1yRysrK7LjjjnnooYdy++2355vf/GbTFp1JMmjQoPzqV7/KiSeemGHDhqWqqirXXHNNPvjgg5x66qlt/K4BAAAAAKsSAR9l4/3338+PfvSj/O53v8vMmTOz5ZZb5sQTT8xBBx202LEPPvhgfvKTn+S5555LVVVV9thjj5xxxhlN2+clyT//+c/st99+Cz3HPvvsk+uuuy5J8swzz+Suu+7K448/nn/+85/p0KFDPvnJT+akk05K7969m42744478qc//SkvvPBC3nnnnWywwQbZa6+9MmTIkKy//vpL+W7A6uuCCy7I66+/3vTzvffe2xTw/eEPf8gmm2ySxsbGNDQ0pFgsNvWrrq7OjTfemB/+8IcZOXJk5syZk+222y7XXHNN9txzz2bX6NmzZ372s59l7NixOfnkk9OhQ4fsu++++c53vpPq6upmfb/73e9mgw02yC233JK33347G2+8cc4555wMHDiwWb8uXbrktttuyw9+8IOceeaZaWhoyC677JKbb7453bt3X95vEwAAAACwCisUP3r3czX39NNPJ0l23HHHElfCgnzjG9/I008/nTPOOCNbbLFF7rnnntxxxx0ZM2ZMDj744IWOu//++3Pqqadmv/32y1e+8pW89957+fGPf5xp06blzjvvzGabbZYkqaury3PPPbfA8ddcc00uuOCC9O/fP0nygx/8II899lgOPvjgbLPNNpkzZ07Gjx+fiRMn5gc/+EG+/OUvN43/9Kc/nZ49e6Zv377ZYIMNMmXKlFx55ZVpaGjIhAkTsu666y7fNwqA5cLvBQAArAh+72xb0+64PPPeeX3xHVcD7dbdOF2OHFbqMlgMc/Y/VqY5O3fu3EyZMiVrrd81aefJZ0nSsWqNdO24duZNezPFefWlLmelUGhXlXZdNlhkn/lzqVu3bmnfvn2L40vye4EVfJSFiRMn5qGHHsoll1ySfv36JUl69eqVf/3rX7n44ovzxS9+MZWVlQscO2bMmHzyk5/Mj3/84xQKhSTJrrvumgMOOCCXX355LrnkkiQfru7ZZZddWoy/5JJL0qFDh6brJsnxxx+fM888s1m/vn375tBDD81PfvKTZgHfhAkT0rVr16af99xzz/To0SNHHHFEbr/99gwePHip3hMAAAAAAGD1VPKo+dVXX815552XQw45JD169GgWoizO3XffnQMPPDA77rhj+vXrl9/97ndtWCmldN9996Vjx4458MADm7Ufdthheeutt/KPf/xjgeOmT5+eKVOmpE+fPk3hXpJsvPHG+eQnP5k//OEPaWhoWOh1p06dmr/+9a/5whe+kLXWWqup/aOB3XyVlZXZfvvt8+9//7tZ+4L67rDDDqmsrMwbb7yx0GsDAAAAAAAsSMlX8L388suZOHFidt555zQ2Nqa1O4bee++9Oeuss3LiiSdm7733zv3335/TTz89a6+9dvbZZ582rpoV7eWXX0737t3Trl3zKbvNNts0Hd9tt91ajKuv/3Bp8MefmTW/bc6cOZk6dWq6deu2wOveddddKRaLOeKIIxZb47x58/LEE080e67fwjz22GNpaGjIVltttdi+AAAAAACsnCoLFan4yOKSFamxWExDsXGpxz/2Pw9nwu135sXnXsis997L2uvUZNsePdLvsC+n5969my2aWRmc8/0f5NkXX8qEm65b7ud+7G9/zzdOOyNrrblmfn/HrVln7bVbHBt/zZXZYdsPM4mvDf1WHv/7fxYerb3WmunerVuGnDYsffr0We71LUjJA77Pfvaz2X///ZMkZ511Vp555plWjbv88stz4IEH5owzzkjy4XaNU6ZMydixYwV8q6AZM2Zkk002adG+zjrrNB1fkHXXXTedOnXKk08+2ay9trY2L7300iLHNjQ05O67786WW26Z3XfffbE1XnHFFXn11Vfzk5/8ZJH9Zs2alQsuuCCf+MQncvjhhy/2vAAAAACwqmksNqaiUPIN5mCZVBYqssFanVJZUZq53NDYmDdnzViqkO+6K6/Kz2+8Ofvs2yenfvv0dO26bqZPm5aHJj6Q84afme+N+UF67bNXG1S99E762sDMmTOnTa8x6/33c/Mv7sypx399sX133XGHDB9yUpJkZu17+cWv7sngwYNz++23p0ePHm1aZ7ISBHwVSzHxX3vttbzyyiv51re+1ay9X79+OfvsszNt2rR06dJleZXISmJR3xZY2LGKioocc8wxufLKK/OTn/wk/fv3z6xZs3LRRRdl7ty5TX0W5MEHH8ybb76Z73znO4ut7Y477shVV12Vb3zjG02B9YJ88MEHGTp0aP71r39l3LhxWXPNNRd7bgAAAIDFEZZQbioKFbn1Hw/mzVkzS13KSmHb9TbKFz/ZcocyVm4VhUIqK0ozlzdYa50M2PnTqSgU0tC6jRGbPPKX/8nPb7w5A4//Ro47cVCzY333/2wOO/qokq1KXJTNNt6oza/Rc7ddc+tdd+erRx2ZmrXXWmTftddaMztv/58gr3fPPdPr8wflj3/84+oR8C2NV155JUmy5ZZbNmvv3r17isViXnnlFQHfKqZTp04LXGk3c+aHH5rzV/ItyJAhQzJ79uz89Kc/zdixY5Mk++67bw477LDccccd2WCDDRY47s4770xVVVW+/OUvL7K2u+66K+edd16OOuqoRYaBdXV1GTJkSJ544olcffXV2XnnnRd5XgAAAIDWEpY0JywpD2/OmpnXa6eVuoyVwvpr1pS6BJZBuc3lu24bn67rds2x3zhugce37bFd0/9+5C//k1+O/0VeeXlS6urqstkWW+SrJw7Kvn36NvX5yfXjcuP42/PX//6vZufZ4/MH5Wv9v5Ih/3edJ596Jpf/7Nq8OOmVNDY2ZuNPbJiv9T8yh3zhgFYd//gWnW+/824uv+b6PP63f+Ttd9/NBuuvl8/v2yeDv/7VZo/t2uHT++VbJ5+Q2XPn5vZf/SaNjY3Zd6/eGXH60HTs0KFZzV87+sh867zv5eY77mqqu7Wqq6tTVVWVefPmLdG4pVWWAd/8UKempvmH3vyQZ/7xpVEsFjN79uylL442seWWW+bee+9NbW1ts+fwPf3000mSTTfddJF/bsOGDcsJJ5yQ119/PZ06dcp6662XwYMHZ+ONN05NTU2LsdOmTcuf/vSn9O3bNx06dFjouX/1q1/le9/7Xg4++OCceeaZC10eXFdXl9NPPz2PP/54Lrvssuy8887mGUkWvTIVWLTWPrd3Wc7v7ygAAOWk3G4wtyVhCcCCNcybl2eeejp9PrtvKtstPiJ641//Su9P75MjBxyTiopCHvufR3LON4fnip/9NJ/r85lWX3fW++9nyJkjsuuOO+Ti756T6qqqTP7fV1M7a1arji/I9Jkzs07N2vn2qSenZu218+pr/8yVN4zLO9OmZ+TZ327W97Zf/iq777xjLjrnzEyZ+lou/ek16dqlc04/+YRm/Tp36pSjDvlSbrnjrgw88vBFruIrFpN58xqSJLXvvZdb7pqQuXPnLnKXv+WpLAO++T5+023+jb5luRlXX1+f559/fpnqYvnbaqutMnv27Nx0003p3bt3U/v48ePTuXPnVFVVtfrP7Z133slf//rXPProoxkwYMACx91zzz2ZN29edt9994Wed+LEifnZz36WffbZJ0ceeWReeOGFBfarr6/PpZdemmeffTann356OnfubI6RJKmqqkqPHtunXbvKUpcCZWfevIY899yzqa+vb9PrfPTbXgAAAADlrnZmberr6rLeBus3ay8Wi2lsaGj6uVBRkYqKinz5K0c0tTU2NmaX3XfL/74yJb+66+4lCvj+97V/5r1Z7+ebJx2fT3b/cHfGXp/ardXHF+ST3bfMt4ec3PTzrjvukA4d2uec7/8g55w+NB3at286tm7XzvnBeSOSJPv03DPPPP9i/vvPD7QI+JLk68d8Jb+Y8OvccucvM/jrX13o9R985NHs8pnPN/3crl27nHfeedlhhx0WWffyUpYB30dX6q277rpN7bW1tUlaruxbElVVVdlqq62WrUCWu+222y4PPPBAxo0bl06dOmXTTTfNvffem3/84x/5/ve/n+233z5Jcv755+eee+7Jr3/962y00Yf78T7++ON59tlns/XWW6dYLOaZZ57JjTfemL333jvDhg1LZWXLcOWcc87JhhtumKOOOmqBz+i77777cs0112TbbbfNN77xjTQ0NKThIx9+2267bdNN4WHDhuUf//hHjj/++Oywww7Nbkavueaa6d69+3J9rygfhUIh7dpV5vs3/DpT33in1OVA2dhsw3Vzzte/1PS53lYmTZrUZucGAAAAKIWFLZR68I9/zvfOPrfp50OOPCxDv31G3n7zrVz/06vz5F8fz7R33m0av+1222VJbLrxRllrzTVz4SWXZ8ARh2bPXXdJl86dWn18Ya/lljt+mTt+fU9e//cb+aCurunYP//172y9Zbemn/fa41PNxnbfYvPcN/GBBZ63a+fOOfJLB+WW2+/KwCMPW+j1d9tph3xn6OAkyezZs/Pgo4/nggsuSPv27XPIIYcssvbloSwDvvnP3nvllVeahSOTJ09OoVBo8Wy+JVEoFNKxY8dlrpHl78orr8xll12Wq666KjNmzMiWW26ZSy+9NAcddFBTn4qKijQ0NKR9+/ZNf45rrbVW/vSnP+Xaa69NXV1dtthiiwwbNiwDBw5MVVVVi+s8+eSTmTJlSoYMGZK11lrw8tuHH344jY2Nef755/P1r3+9xfE//OEP2WSTTZIkDzzw4YfEtddem2uvvbZZvz333DM333zz0r0hrDKmvvFOXn7tzVKXAWWnw8f2SF/ebM8JAAAArGrW6bROqqqr8/abbzdr33WP3fOTGz+8f33e8DOTfLhi7/8NPzPvz5qVr514fDbadJO0b98+4352bd5+860lu+7aa+eaSy/OT66/MWePHJ2GhobsttOOGfHNU/PJ7lsu9viC3Hz7XRlz5dX5+tFHZc/ddknN2mvl2RdezMhLxzYL+5Jk7Y/d66+qqkpd3cJ3hvrGMf1z+6/uya133p3ddt5xgX3WWnPN7LDtNk0/99xzz/zvv9/M6NGj86UvfanN7y2VZcC36aabZsstt8xvf/vbfO5zn2tqv+eee7LTTjulS5cuJayOtrLmmmvm3HPPzbnnnrvQPqNHj87o0aObte222265/fbbW32d3XbbLS+++OIi+yzoOguzuHMBAAAAAMCKUNmuXXbYacf87fHH09DQ0LTD3do1Ndmmx4e7I7b7v4Ux/3rtn5n04ku54Iejs3ffTzed44MPPmh2zjWqq5ueRTdfXV1d5s5t3m/HHtvmqjGjM/eDD/LYk3/PmJ9cldNGnJd7f3FLq45/3O//PDH77t07p598fFPbK//76tK8LS2s27VLjvjSQbnp9ruyzVat34Wve/fu+dOf/pR333232Q6UbaHl3oMr2Jw5c3Lvvffm3nvvzeuvv55Zs2Y1/Txt2ocPBR4xYkR69OjRbNxpp52W3/3ud7nsssvy6KOP5qKLLspDDz2U0047rRQvAwAAAAAAYKV3+DH98+7b7+TnN960yH7zg7yqqv+sFXvz32/k2X883azfBuutm/r6+kx9/V9NbQ8//uRCH63Sfo010qd3zxz15S99uLXmB3VLdPw/9dW12KXvnvv+sMjXtCQGHdM/H3zwQW698+5Wj3n55ZdTVVW10N0Bl6eSr+B79913M2zYsGZt83++6aab0rNnzzQ2NjZ7vlmSfOELX8jcuXNz1VVX5brrrsvmm2+eyy67LPvss88Kqx0AAAAAAKCc9Npnr/Q/7tjcePW1mfTSy9l3//3Sdd2ueX/W+3n67//ItHenpUPHjtl0i82z3vrr59ofX5XGhsbMmTsnN/3suqy73nrNzvfpXnumQ4f2Of/iSzLomP554+23c8sdv2wWvk38n0fyy//6Xfb79D75xAbr551p03LbXXdn1x23zxprVC/2+IL0/tRuueXOu3PbXROy+aab5L/uuz+v/fNfC+y7NNZbt2sOP/ig3HbXggO+92a9n388+1yS5P3/ewbfxIkT85WvfCXt27dfbnUsTMkDvk022WSpt0M89NBDc+ihh7ZVaQAAAAAAAIu0wVrrlN01jx9ySnbYeef8+s67MvbiS/L+rFlZe52afHLbbTL83LPzmc/vn4qKinz34otyxcWX5Hsjzs1662+QAd84Ln9//Im8/MJ/cp1O66yTH114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3rx5ef3115cq4CsUCunYseMSj4OlYZtByo05S7lZljm7JGFyxVJfBQAAAADKUJ8+ffLII49k+vTpTW333Xdf6urq0rdv34WO23jjjZMkzz77bLP2Z555JkmyySabtEG1AAAtWcEHAAAAwGqlf//+ueWWWzJ48OAMHjw47777bkaPHp2DDz642RadI0aMyIQJE/Lcc88lSXbYYYfstNNO+e53v5t33nkn3bp1y9NPP50rr7wyX/ziF9OlS5dSvSQAYDUj4AMAAABgtVJTU5Nx48Zl5MiRGTp0aNq3b59+/fpl+PDhzfo1NjamoaGh6efKyspcddVVufzyy3PNNdfknXfeySc+8Ykce+yxOfnkk1f0ywAAVmMCPgAAAABWO926dct11123yD6jR4/O6NGjm7V17do13/ve99qyNACAxfIMPgAAAAAAACgjAj4AAAAAAAAoIwI+AAAAAAAAKCMrRcA3ZcqUDBo0KLvsskt69+6dkSNHZu7cuYsdN3v27IwZMyb7779/dt5553z+85/PFVdckbq6uhVQNQAAAAAAAKx47UpdQG1tbY477rhstNFGGTt2bKZNm5ZRo0ZlxowZGTNmzCLHnn/++bn//vtz+umnZ+utt85TTz2VsWPHZubMmTn33HNX0CsAAAAAAACAFafkAd/48eNTW1ubCRMmpEuXLkmSysrKDB8+PKecckq6d+++wHHz5s3Lvffem+OPPz4DBw5MkvTq1Sv/+te/8tvf/lbABwAAAAAAwCqp5Ft0PvDAA+ndu3dTuJckBxxwQKqrqzNx4sSFjisWi2loaMjaa6/drL2mpibFYrHN6gUAAAAAAIBSKnnAN3ny5Bar9Kqrq7PZZptl8uTJCx1XVVWVww47LDfffHP+8Y9/5P33388jjzyS22+/PQMGDGjrsgEAAAAAAKAkSr5FZ21tbWpqalq019TUZObMmYsce/755+e73/1uvvKVrzS1DRw4MKeeeupS11MsFjN79uylHt8ahUIhHTp0aNNrwKpszpw5q8RKXZ8FsGza+rOgWCymUCi02fkBAAAAYGmVPOBbmNbcVBszZkz+/Oc/58ILL0y3bt3y7LPPZuzYsampqclpp522VNetr6/P888/v1RjW6tDhw7p0aNHm14DVmVTpkzJnDlzSl3GMvNZAMtmRXwWVFdXt+n5AQAAAGBplDzgq6mpSW1t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+AAAAAAAAKCMCPgAAAAAAACgjAj4AAAAAAAAoIwI+AAAAAAAAKCMCPgAAAAAAACgjAj4AAAAAAAAoIwI+AAAAAAAAKCMCPgAAAAAAACgjKwUAd+UKVMyaNCg7LLLLundu3dGjhyZuXPntmrs3XffnQMPPDA77rhj+vXrl9/97ndtXC0AAAAA5c79KACgnLUrdQG1tbU57rjjstFGG2Xs2LGZNm1aRo0alRkzZmTMmDGLHHvvvffmrLPOyoknnpi99947999/f04//fSsvfba2WeffVbQKwAAAACgnLgfBQCUu5IHfOPHj09tbW0mTJiQLl26JEkqKyszfPjwnHLKKenevftCx15++eU58MADc8YZZyRJevXqlSlTpmTs2LF+oQIAAABggdyPAgDKXcm36HzggQfSu3fvpl+mkuSAAw5IdXV1Jk6cuNBxr732Wl555ZX069evWXu/fv3y1FNPZdq0aW1WMwAAAADly/0oAKDclTzgmzx5cotvRVVXV2ezzTbL5MmTFzrulVdeSZJsueWWzdq7d++eYrHYdBwAAAAAPsr9KACg3JV8i87a2trU1NS0aK+pqcnMmTMXOm7+sY+PXWeddZodXxL19fUpFot56qmnlnjskioUCumzUUXmbbhWm18LVhXtKiry9NNPp1gslrqU5aZQKOTYvttnXsO2pS4Fyka7ysoV8llQX1+fQqHQptcAAKA0VsX7UYVCIb3bb5CG6vWW6TyriqrGdnn66afTuPmeyaaNpS5n5VBRkddXovsq5mxz5uwCmLMrNXN2AZbDnF2S+1ElD/gWplgstupFfLzP/DduaW7IzR+zom7mrdW+aoVcB1Y1q9oN905rdyx1CVCW2vqzoFAorHKfNwAALFq5349aq7r9Mp9jVVPRwZfrP25l+v855mxL5mxL5uzKzZxtaVnm7JLcjyp5wFdTU5Pa2toW7e+9994iH2j80W9Grbvuuk3t88+1oG9hLc6uu+66xGMAAAAAKC/uRwEA5a7kz+Dr3r17i73N6+rqMnXq1EX+QjV/r/OP720+efLkFAqFFnuhAwAAAEDifhQAUP5KHvD16dMnjzzySKZPn97Udt9996Wuri59+/Zd6LhNN900W265ZX772982a7/nnnuy0047pUuXLm1WMwAAAADly/0oAKDclTzg69+/f9Zee+0MHjw4Dz74YCZMmJALL7wwBx98cLNvTI0YMSI9evRoNva0007L7373u1x22WV59NFHc9FFF+Whhx7KaaedtqJfBgAAAABlwv0oAKDcrRTP4Bs3blxGjhyZoUOHpn379unXr1+GDx/erF9jY2MaGhqatX3hC1/I3Llzc9VVV+W6667L5ptvnssuuyz77LPPinwJAAAAAJQR96MAgHJXKBaLxVIXAQAAAAAAALROybfoBAAAAAAAAFpPwAcAAAAAAABlRMAHAAAAAAAAZUTABwAAAAAAAGVEwAcAAAAAAABlRMAHAAAAAAAAZUTABwAAAAAAAGVEwMcKM3HixJxwwgnp1atXtt9+++y11145+eST8+c//znFYrHU5bVw1llnpV+/fm1y7kcffTTbbLNNdt9998ycOXOBx55++ummtoEDB2abbbZp+udTn/pU+vfvnwceeKBN6oNVyRVXXJFdd921RfvFF1+cbbfdNr/4xS9yxRVXZJtttsmAAQNaNX7+38WHH364Rf9tttkm11133fJ7AQAAAAAAHyPgY4W49NJLc+KJJ2aNNdbIeeedlxtvvDH/7//9v6y55po55ZRTMnHixFKX2MLgwYMzZsyYNr3GrFmzcuONN7aq72677ZZf/OIX+cUvfpFLLrkknTp1yuDBg/Pcc8+1aY2wKrr00ktz3XXX5bzzzstRRx3V1P74448vMLRbmB//+MdtUR4AAAAAwCK1K3UBrPr+/Oc/5+qrr86pp56aoUOHNjv2hS98Iccdd1wqKla+rHmzzTZr82v06tUrN998c77+9a+npqZmkX1ramqyyy67NP3cu3fvfOpTn8of//jH9OjRo40rhVXH5ZdfnquvvjrnnXdejjnmmKb2jh07Zuutt85PfvKT9O7de7Hn6dWrVx555JE88sgj6dWrV1uWDAAAQBubNGlSkqRQKKR79+4lrgZar7GxcaW8twofN2PGjMyePTsVFRXZcMMNkyTFYjGFQqHElZUvf/NpczfccEPWW2+9nHLKKQs8vtNOO2WHHXZI8mEY+PWvfz29e/fObrvtliOPPLLFNpQL225v1113zRVXXNH08xNPPJEBAwZk9913z6677pqDDz44d999d6uPf3yLzrfeeitnn3129ttvv+y00075/Oc/n0svvTR1dXXN6thmm21yzTXXZOzYsdlrr73Ss2fPnH322Zk9e3aLmr/xjW+koaEh48aNW9RbuEDV1dWpqqrKvHnzlngsrK5+/OMf58orr8w555yzwO04hwwZkr/+9a959NFHF3uuPn36ZKeddspPfvKTtigVAACAFWTkyJEZOnRojjzyyAwcODAjRozI888/v1I+UgaS5Jprrsktt9ySJKmoqEhjY2OJK4JFu+KKK3LaaafloIMOyqBBg3LttdcmiXBvGVnBR5uaN29ennzyyRxwwAFp127x0+2f//xnPvOZz+Qb3/hGKioq8sADD+TEE0/MuHHj0rNnz1Zfd9asWTnppJOy++6759JLL011dXUmTZqU2traVh1fkOnTp6dTp045++yzU1NTk//93//NFVdckbfffjujRo1q1vfWW2/N7rvvntGjR2fKlCn54Q9/mK5du2b48OHN+nXp0iX9+/fPuHHjctxxxy1yFV+xWGwK82prazNu3LjMnTs3+++/f6vfF1idXXXVVbniiity9tln56tf/eoC+/Tt2zc77rhjfvzjH7fqM2fIkCE56aST8uijjy7RZxQAAAArh5/85Ce55557cv7556eqqipvv/12Lr/88jz99NM5/vjjc/DBB1sdxUrlxz/+cX784x+nW7duWWONNXLkkUc2hXzmKiujiy66KL/97W/z1a9+NZ/97Gfz9NNPZ8yYMVlzzTVz9NFHl7q8sibgo03NmDEjdXV1+cQnPtGsvVgspqGhoennioqKVFRU5Nhjj21qa2xsTM+ePTNp0qTcfvvtS3TzfMqUKXnvvffyrW99K9tss02SNNtyb3HHF2SbbbbJmWee2fTzbrvtlg4dOuSss87Keeedlw4dOjQdW3fddXPJJZck+XCVz9NPP53f//73LQK+JDn++OPz85//PDfddFNOPfXUhV5/4sSJ2X777Zt+rqqqynnnnde0+hFYuNmzZ+eyyy7LEUccka997WuL7DtkyJCcfPLJeeyxx7Lnnnsusu++++6bHXbYodWBIAAAACuPxsbGPPvss+nXr18+97nPpbKyMknymc98JieddFJ++tOfpr6+PocddpjghJXCww8/nAkTJmTfffdtWgBQLBbzla98RcjHSunXv/51Jk6cmAsvvDCf/vSn065du7z22muZMWNG7r///nz5y19O+/btreRbSv6206bmb2Xw8b+gv//977P99ts3/TNy5MgkyRtvvJEzzzwzn/70p9OjR49sv/32+ctf/pIpU6Ys0XU322yzrLXWWjn//PPz29/+NtOmTVui4wt7LTfeeGO++MUvZqeddsr222+f4cOHZ968eXnttdea9d17772b/bzVVlvljTfeWOB5u3btmqOOOirjxo3Le++9t9Dr77777rnzzjtz5513Zty4cRk4cGAuuOCC/OpXv1ps7bC6a9++ffbYY4/cc889eeKJJxbZ9zOf+Uy23377/PjHP27VuQcPHpzHHnssf/3rX5dHqQAAAKwgH3zwQSZPnpx58+Y1hXt1dXXZYIMNcv3116empibXXntt/vKXv5S4UkgaGhryxhtvpFAo5Nvf/nZGjx6dzp0756abbsrtt9+exHadrFxmz56diRMnplu3btlll12advjbdNNN07dv3zz55JOpra0V7i0DAR9tqnPnzqmurm4RbvXu3bsprFpvvfWSfPitqVNOOSVPPPFETjvttNx00025884706dPnxbPuVucddZZJzfccEPWXHPNfOc738nee++dgQMH5sUXX2zV8QUZN25cfvCDH2S//fbLlVdemTvuuCPnnXdekg9/Ifyoj2+1WVVVtcjXcPzxx6euri433XTTQvusvfba2XHHHbPjjjumV69eOfPMM9OnT5+MHj3anvCwGBUVFbnqqquy5ZZb5uSTT84LL7ywyP6DBw/Oo48+mscff3yx595vv/3So0ePVgeCAAAArBw6dOiQfffdN0888UReeumlJEl1dXXmzZuXLl265Kqrrkp9fX2uu+66ElcKSWVlZXbZZZeMHTs23bt3z2abbZYRI0akS5cuQj5WSh07dsyuu+6az3/+8+ncuXOSNM3N7bbbLo2NjYtc8MLiCfhoU+3atctuu+2Whx9+uNmWnOuss05TWFVdXZ0kefXVV/Pcc8/lrLPOypFHHpk999wzO+64Y+bOndvsnGussUbq6+ubtdXV1WXOnDnN2nbaaadce+21efzxx3PVVVfl3XffzZAhQ1p9/OPuvffefPazn80ZZ5yRffbZJzvttFM6duy41O/NR6233nr5yle+knHjxmXWrFmtHte9e/dMmzYt77777nKpA1Zla621Vq699tp06dIlxx9/fKZOnbrQvvvvv3+22267Vod2Q4YMySOPPNKqQBAAAICVx5577pm33nor//Vf/5UZM2Yk+fB+Vn19fbp06ZIxY8bksccey913313aQiFJt27dst122zU9/mi77bZrFvL94he/SPJhyPfxe6WwIs1fkHLsscfmsMMOa2qfv4XspptumkKh0PS5y9IR8NHmvv71r+ett97KVVddtch+81fBVVVVNbW9/vrr+dvf/tas3wYbbJD6+vpmN+f/53/+Z6Gr2Nq3b5++ffvm6KOPzj//+c8Wq+0Wd3y+uXPnNqstSX7zm98s8jUtiRNOOCFz587NzTff3OoxL7/8cqqqqrLWWmsttzpgVda1a9dcf/31qaioaPpsWpghQ4bk4YcfXuyWnsmHq/i23XZbq/gAAADKzH777ZdjjjkmV199dX71q181rSapqqpKsVhMjx49st122zWt8IOVQaFQSGVlZYrFYrbddtuMGDEinTt3zs0335w77rgj9fX1ueWWWzJmzBg7f1ESi9t2s6GhIfX19c1Wm06YMCGXXnqpObsE2pW6AFZ9++67b0488cSMHTs2L7zwQr7whS9k/fXXz3vvvZfHH388b7/9dtZcc81sueWW2XDDDXPJJZeksbExc+bMydixY7P++us3O1+fPn3SsWPHnHvuuTnhhBPyxhtv5KabbmoWvv35z3/OnXfemf333z8bbbRR3nnnndxyyy3ZbbfdssYaayz2+ILstddeuemmm3LLLbdkiy22yG9+85u8+uqry+19Wn/99fOVr3xloQFfbW1t/v73vydJ3n///UycODETJ07MV77ylbRv33651QGruo033jjXX399BgwYkEGDBuWWW25ZYL/9998/22yzTR5++OHFrtYtFAoZMmRIhg4d2hYlAwAA0AYaGxtTUVGRYcOGZebMmfnhD3+YDz74IF/60pey4YYbplAopKKiImuuuWbTM/pgZVIoFJpCvnPOOSejRo3KTTfdlAcffDD//d//nZNOOsnzzVjpNDY2prq6OlVVVU079f32t7/NWWedlRNOOMGcXQICPlaIM844I7vvvntuvfXWXHDBBZk1a1bWWWedbL/99rnoooty0EEHpaKiIldccUW+973vZdiwYfnEJz6RU045JY888kieeeaZpnN17tw5Y8eOzQ9+8IMMGTIk2223XX74wx/m6KOPbuqz2WabpaKiIj/60Y/yzjvvpHPnztlnn33yrW99q1XHF2TIkCGZPn16xo4dmyQ54IADcu655+bkk09ebu/TCSeckF/84hcLfF7fk08+maOOOirJh6sON91003znO9/JwIEDl9v1YXWx1VZb5Zprrslxxx2Xk046KbvttluLPvNDu9NOO61V5/zc5z6XT37yk77VCQAAUCYqKipSLBZTKBRy3nnnpbq6Oj/60Y/y4osv5tBDD83mm2+exx57LM8991yOPfbYUpcLC/TRkO+0007L8OHD89///d/59re/nUGDBpW6PGihoqIi66yzTiorK/Pmm2/mv/7rv3LmmWdm6NChi3yEFi0VitY7AgAAAADk1ltvzYQJE/Lss8+mU6dOSZKvfvWry/UL3tAW3nvvvUyYMCEXXXRRzjzzzHzta19L8p+VqrAymTt3br7whS9kxx13zJ///OeceOKJOfXUU5OYs0tCwAcAAAAArNY+ekP5rbfeyptvvpn3338/NTU16dGjR4s+sLKZNGlSjjnmmBx99NE5/fTTk5izrJwaGxsza9as7L///qmtrc3w4cNz/PHHNx0zZ1tPwAcAAAAArPLmb8e5NMfddKYUFjdnP2rOnDmZOnVqttlmmyTmLKWxJHP2qquuSqFQyEknnZTEnF0aAj4AAAAAYJXy0ZvM06dPT+fOnUtcESza8pyzghJWhKWdswuan+bs0vGOAQAAAACrjI/edP7v//7vnHbaabnvvvuW+Bwf/Te0peU9ZwUltLVlmbPz5+f8OdvQ0GDOLiXvGgAAAACwyph/0/mXv/xlzj777KYtC1vrozeup02bttzrg48zZyk3y3POzpgxY3mXt9oQ8AEAAAAAq5THH388l1xySYYOHZpvfvOb+dznPpckmTt37iLHffSm8zXXXJP9999fYMIKYc5SbszZ0mtX6gIAAAAAAJanF154ITU1Nfnyl7+ctdZaKw0NDRk7dmxefvnldOrUKUcddVR23nnnZmM+etP5pptuytixY3PWWWelS5cupXgJrGbMWcqNOVt6Aj4AAAAAoGx99IbxfA0NDWloaMgf//jHVFZW5vrrr88bb7yR3XffPb/73e/y5ptv5pprrmn2LKj557j55pszatSofO9738uRRx65wl8Pqz5zlnJjzq6cBHwAAAAAQFn66A3j5557Ll26dMmGG26Yz372s/nd736XH/zgB1lrrbWyzTbb5IYbbkiXLl3yX//1X/nud7+bf//739l4441b3HS+6KKL3HSmzZizlBtzduUl4AMAAAAAys5Hbxj/+te/ztixY7PNNtvk+9//fjbddNNcccUV+de//pV27dpl++23T5J88MEHeeWVV7LVVlulQ4cOSdJ0jhtvvDEXX3yxm860GXOWcmPOrtwEfAAAAABA2fnoTedzzjkn3/zmN7PbbrulU6dOaWxszHrrrZf11luvqf+bb76ZBx54IOPGjcuwYcOanvlULBYzffr03Hbbbfnud7/rpjNtxpyl3JizK7dCsVgslroIAAAAAIAlNXXq1AwePDgHHXRQBg0alOrq6iTJSy+9lPbt22f99ddP+/btc//99+fnP/95XnnllRx99NE58cQTkzRfnTJ9+vR07ty5ZK+F1YM5S7kxZ1deVvABAAAAAGWpWCxm5syZ2WyzzVJdXZ0333wzo0aNylNPPZW33347Rx11VIYPH56tttoqW2yxRfr375/Pfe5zSZLGxsZUVFQ0nctNZ1YEc5ZyY86uvKzgAwAAAADK0uTJk3PaaaelW7duWWONNfLMM88kSY477ri89957ueyyy3L11Venb9++qaura1p58vGbzrCimLOUG3N25eXdBQAAAABWagtbo9C9e/eceOKJmTlzZmbMmJF99903v//973PMMcfkmGOOyUYbbZTZs2cnSdNN5yRuOtPmzFnKjTlbfmzRCQAAAACstD76/KYnnngiU6ZMydtvv50dd9wx++yzTw455JD06dMna621VqqqqpIks2fPzn333Zd58+Zlww03LGX5rIbMWcqNOVuebNEJAAAAAKz0JkyYkFGjRmXLLbfMtGnTUllZmX322ScjRoxIkjQ0NKSysjLPPfdcHn/88fzoRz/KKaeckhNOOKHElbO6MmcpN+ZseRHwAQAAAAArnY+uKPnDH/6QESNG5MQTT8ygQYPyxBNP5LjjjkuHDh1y4IEH5sILL0ySTJo0KaeddloqKipy5JFH5rjjjkviWVCsGOYs5cacLW8CPgAAAABgpfHss89myy23TIcOHZIkM2bMyJgxY7L22mvnzDPPzAsvvJBjjjkm++23Xzp27Jh77rknhx56aM4999wkyaOPPprq6ursuuuuSdx0pu2Zs5Qbc3bV4B0HAAAAAFYKt912Ww4//PDce++9+eCDD5IkVVVV2XbbbXPIIYfk3XffzbBhw7Lffvvlhz/8YU499dSst956ufXWW3PWWWclSXr27Nl007lYLLrpTJsyZyk35uyqw7sOAAAAAKwUjjnmmOyyyy754Q9/mN/+9reZM2dO1lxzzXz5y1/Otttum7/85S9JkkGDBiVJ1ltvvey9997ZfPPN86c//SkPPfRQs/PN33oO2oo5S7kxZ1cd7UpdAAAAAABAXV1dqqurM378+BxzzDEZNWpUkuTAAw/MWmutlSR57bXXMmvWrHTu3DlJ8sEHH6S2tjZHHHFE9thjj+y8884lq5/VjzlLuTFnVy1W8AEAAAAAJVUsFlNdXd3082233Zbtttsuo0aNyu9///vMnTs3SbLXXntlxowZufHGGzNx4sT8+te/zl/+8pfssssuTTedGxsbS/IaWL2Ys5Qbc3bVUygWi8VSFwEAAAAArJ6KxWLTFm9/+tOf8vjjj+fzn/98dt555xx99NF55ZVXctZZZ+WAAw5Ix44dc9ttt+Wiiy5KVVVVCoVCTjrppJx00kklfhWsTsxZyo05u2oS8AEAAAAAJffLX/4y3//+9/PFL34xBx10UHr16pXkw+dFTZ48OWeffXa+8IUvZI011sjLL7+cd955J2uuuWZ22mmnJB+uKKmosGEZK445S7kxZ1ctAj4AAAAAoKQef/zxDB48OKecckoOO+ywrLPOOs1WnHz05vPnPve5rLnmms3Gu+nMimbOUm7M2VWPPw0AAAAAoKSee+65rLfeevn85z+fddZZJ0lSKBRSX1+f5MNnRW277bb57ne/m3vuuScNDQ3NxrvpzIpmzlJuzNlVjz8RAAAAAKCkJk2alNmzZ2fjjTdO8uHzopKkqqoqSTJr1qyMGzcuW265ZYrFYiorK0tWKyTmLOXHnF31CPgAAAAAgJLaaqutMmvWrDz88MNJPlxVMv/m82uvvZaf/vSnefPNN3P33Xenf//+pSwVkpizlB9zdtUj4AMAAAAASqpv374pFou56aabMmnSpCQf3nyuq6vLQw89lD/84Q958803m/rPvykNpWLOUm7M2VVPoehPCQAAAAAosQcffDBDhgxJjx49cuCBB+b/t3eHuAlEQQCGZ6Eai92QcANugiAEQ4LlElyFhGC4CsFikByAIMDsUoFpk6ataLPM5vvse2LEuD9vdzAYxH6/j/V6HcvlMhaLRdMjwid2lmzsbLsIfAAAAADASzgcDrFareJ8Psf9fo+yLGM8Hsd8Po+I54uSoiiaHRI+sLNkY2fbQ+ADAAAAAF7G9XqNy+USt9ster1e9Pv9iIio6zo6HX8c4vXYWbKxs+0g8AEAAAAAL82LErKxs2RjZ/MR+AAAAAAAACARby0BAAAAAAAgEYEPAAAAAAAAEhH4AAAAAAAAIBGBDwAAAAAAABIR+AAAAAAAACARgQ8AAAAAAAASEfgAAAAAAAAgEYEPAAAAAAAAEnlregAAAAAAAGiz0+kUk8nk2zubzSZms9m3d3a7XZRl+YeTAVkJfAAAAAAA8I+qqorhcBjb7fbL8+l0GkVR/Hinqqr/HBNIxCc6AQAAAAAAIBGBDwAAAAAAABIR+AAAAAAAACARgQ8AAAAAAAASEfgAAAAAAAAgEYEPAAAAAAAAEhH4AAAAAAAAIBGBDwAAAAAAABIR+AAAAAAAACARgQ8AAAAAAAASEfgAAAAAAAAgEYEPAAAAAAAAEnlregAAAAAAAGizbrcbx+MxRqPRl+dVVUVd1z/e6XS82QGeisfj8Wh6CAAAAAAAAOB35H4AAAAAAABIROADAAAAAACARAQ+AAAAAAAASETgAwAAAAAAgEQEPgAAAAAAAEhE4AMAAAAAAIBEBD4AAAAAAABIROADAAAAAACARAQ+AAAAAAAASOQdC3ZHpw/9oK4AAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "\n",
    "# ===================== 数据准备 =====================\n",
    "# 假设已经运行了之前的代码，并得到以下结果：\n",
    "# - y_test: 测试集真实标签\n",
    "# - y_pred_nb: 朴素贝叶斯预测结果\n",
    "# - y_pred_knn: KNN预测结果\n",
    "\n",
    "# 定义函数提取分类报告中的指标\n",
    "def get_metrics_df(y_true, y_pred, model_name):\n",
    "    report = classification_report(y_true, y_pred, target_names=target_names, output_dict=True)\n",
    "    df = pd.DataFrame(report).transpose().reset_index()\n",
    "    df = df[df['index'].isin(target_names)]  # 仅保留类别行\n",
    "    df['Model'] = model_name\n",
    "    return df\n",
    "\n",
    "# 获取两个模型的分类报告数据\n",
    "nb_metrics = get_metrics_df(y_test, y_pred, 'GaussianNB')\n",
    "knn_metrics = get_metrics_df(y_test, y_pred_custom, 'KNN')\n",
    "all_metrics = pd.concat([nb_metrics, knn_metrics])\n",
    "\n",
    "# ===================== 绘图 =====================\n",
    "plt.figure(figsize=(18, 6))\n",
    "sns.set_theme(style=\"whitegrid\")\n",
    "\n",
    "# ---------- 子图1：准确率对比 ----------\n",
    "plt.subplot(1, 3, 1)\n",
    "accuracies = {\n",
    "    'GaussianNB': accuracy_score(y_test, y_pred),\n",
    "    'KNN': accuracy_score(y_test, y_pred_custom)\n",
    "}\n",
    "sns.barplot(x=list(accuracies.keys()), y=list(accuracies.values()), palette=\"Blues_d\")\n",
    "plt.title(\"模型准确率对比\")\n",
    "plt.ylabel(\"准确率\")\n",
    "plt.ylim(0, 1.1)  # 留出空间显示数值标签\n",
    "for i, acc in enumerate(accuracies.values()):\n",
    "    plt.text(i, acc + 0.02, f\"{acc:.4f}\", ha='center')\n",
    "\n",
    "\n",
    "# ---------- 子图3：分类指标对比（精确率/召回率/F1） ----------\n",
    "plt.subplot(1, 3, 3)\n",
    "melted_metrics = pd.melt(all_metrics, id_vars=['index', 'Model'], \n",
    "                         value_vars=['precision', 'recall', 'f1-score'],\n",
    "                         var_name='Metric', value_name='Value')\n",
    "sns.barplot(x='index', y='Value', hue='Model', data=melted_metrics, \n",
    "            palette=\"Set2\", ci=None)\n",
    "plt.title(\"各类别指标对比\")\n",
    "plt.xlabel(\"类别\")\n",
    "plt.ylabel(\"得分\")\n",
    "plt.legend(title=\"模型\", loc='upper right')\n",
    "plt.xticks(rotation=45)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.undefined.undefined"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
